/*
 * Galaxium Messenger
 * Copyright (C) 2007 Paul Burton <paulburton89@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

using Gdk;
using Gtk;

using Cairo;

using Swfdec;
using Swfdec.Gtk;

using Anculus.Core;
using Anculus.Gui;

using Galaxium.Core;
using Galaxium.Gui.GtkGui;

namespace Galaxium.Protocol.Msn.GtkGui
{
	public class WinkDisplay : Gtk.Window
	{
		MsnWink _wink;
		GtkPlayer _swf;
		Gtk.Widget _displayAt;
		bool _useComposite;
		int _width;
		int _height;
		
		[DllImport("libgdk-x11-2.0.so")]
		internal static extern IntPtr gdk_cairo_create (IntPtr raw);
		
		public WinkDisplay (MsnWink wink, Gtk.Widget displayAt)
			: base (wink.Friendly)
		{
			_wink = wink;
			_displayAt = displayAt;
			
			ThrowUtility.ThrowIfFalse ("Swf doesn't exist", File.Exists (_wink.Animation));
			
			_swf = new GtkPlayer (_wink.Animation);
			_swf.FsCommand += OnFsCommand;
			_swf.Invalidate += OnInvalidate;
			_swf.ScaleMode = Swfdec.ScaleMode.NoBorder;
			
			AppPaintable = true;
			Decorated = false;
			SkipTaskbarHint = true;
			SkipPagerHint = true;
			WindowPosition = Gtk.WindowPosition.None;
			TransientFor = (Gtk.Window)_displayAt.Toplevel;
			CanFocus = false;
			AcceptFocus = false;
			
			OnScreenChanged (null);
			
			//TODO: Also call RePosition when the container window moves
			_displayAt.SizeAllocated += delegate { RePosition (); };
			RePosition ();
			
			Realize ();
		}
		
		void RePosition ()
		{
			_width = _wink.Width;
			_height = _wink.Height;
			
			int dispWidth = _displayAt.Allocation.Width;
			int dispHeight = _displayAt.Allocation.Height;

			double scale = 1;
			
			if (_width > dispWidth)
				scale = (double)dispWidth / _width;
			if (_height > dispHeight)
				scale = Math.Min (scale, (double)dispHeight / _height);
			
			if (scale < 1)
			{
				_width = (int)(_width * scale);
				_height = (int)(_height * scale);
			}
			
			if (_width * _height <= 0)
			{
				if (dispWidth < dispHeight)
					_height = _width = dispWidth;
				else if (dispHeight < dispWidth)
					_height = _width = dispHeight;
				else
				{
					_width = dispWidth;
					_height = dispHeight;
				}
			}

			Resize (_width, _height);
			_swf.SetSize (_width, _height);

			int dispX, dispY;
			_displayAt.GdkWindow.GetPosition(out dispX, out dispY);
			
			int dispAllocX, dispAllocY;
			_displayAt.TranslateCoordinates(_displayAt.Toplevel,
			                                0, 0,
			                                out dispAllocX,
			                                out dispAllocY);
			
			dispX += dispAllocX;
			dispY += dispAllocY;
			
			int nX = dispX + (int)((dispWidth - _width) / 2);
			int nY = dispY + (int)((dispHeight - _height) / 2);

			Move (nX, nY);
		}
		
		protected override void OnScreenChanged (Screen previous_screen)
		{
			_useComposite = Screen.RgbaColormap != null;
			
			if (_useComposite)
			{
				Colormap = Screen.RgbaColormap;
				
				_swf.BackgroundColor = 0;
			}
			
			if (previous_screen != null)
				base.OnScreenChanged (previous_screen);
		}
		
		protected override bool OnExposeEvent (EventExpose evnt)
		{
			Context context = new Context (gdk_cairo_create (evnt.Window.Handle));

			if (_useComposite)
			{
				context.Save ();
				context.Color = new Cairo.Color (0, 0, 0, 0);
				context.Operator = Operator.Source;
				context.Paint ();
				context.Restore ();
			}
			
			_swf.Render (context, 0, 0, _width, _height);
			
			((IDisposable)context.Target).Dispose ();
			((IDisposable)context).Dispose ();

			return true;
		}
		
		public new void Show ()
		{
			ShowAll ();
			
			_swf.Playing = true;
		}
		
		void OnFsCommand (object sender, FsCommandEventArgs args)
		{
			if (args.Command == "quit")
			{
				_swf.Playing = false;
				Destroy ();
				_swf.Dispose ();
			}
		}
		
		void OnInvalidate (object sender, InvalidateEventArgs args)
		{
			QueueDraw ();
		}
	}
}
